﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using UCL.Data;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;

namespace UCL.Forms.Components
{
    /// <summary>
    /// 语法编辑器控件
    /// </summary>
    /// <remarks>2014-1-21 by sutroon</remarks>
    public partial class ucCodeEditor : UserControl
    {
        /// <summary>
        /// 设置或获取执行Sql语句的委托方法
        /// </summary>
        public Action<string> RunScriptHandler { get; set; }

        /// <summary>
        /// 获取或设置执行到...菜单的委托方法
        /// </summary>
        public Action<string> RunForHandler { get; set; }

        /// <summary>
        /// 设置或获取显示关于我们的委托方法
        /// </summary>
        public Action ShowAboutMessageHandler { get; set; }

        /// <summary>
        /// 设置或获取数据库帮助类实例对象
        /// </summary>
        public IDatabaseHelper DBH { get; set; }

        /// <summary>
        /// 获取或设置对象名称,用于智能提示控件
        /// <para>如：数据表名称、存储过程名称或视图名称</para>
        /// </summary>
        public string ObjectName { get; set; }

        /// <summary>
        /// 获取或设置对象类型,用于智能提示控件
        /// <para>如：TABLE|PROCEDURE|VIEW</para>
        /// </summary>
        public string ObjectType { get; set; }

        /// <summary>
        /// 获取或设置执行过的Sql命令集
        /// </summary>
        public IList<string> ScriptHistorySet { get; set; }

        public ucCodeEditor()
        {
            InitializeComponent();
            ScriptHistorySet = new List<string>();
        }

        /// <summary>
        /// 控件初始化
        /// </summary>
        public void uc_init()
        {
            // 设置编辑器语法渲染
            editor.Encoding = System.Text.Encoding.Default;
            editor.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy("TSQL");

            // Initialize folding
            editor.Document.FoldingManager.FoldingStrategy = new FoldingStrategy();
            // Update folding markers, call whenever you need to update the folding markers.
            // Especially when the text in the texteditor has been changed.
            editor.Document.FoldingManager.UpdateFoldings(null, null);

            // 坑爹的，事件这么复杂! (2014-1-21 by sutroon)
            // 设置编辑器键盘事件
            editor.ActiveTextAreaControl.TextArea.KeyPress += new KeyPressEventHandler(editor_KeyPress);
            editor.ActiveTextAreaControl.TextArea.KeyDown += new System.Windows.Forms.KeyEventHandler(editor_KeyDown);

            ClickEventHandler();
        }

        /// <summary>
        /// 获取当前选中的Sql命令
        /// <para>如果没有选中任何命令则返回文本框所有SQL命令</para>
        /// </summary>
        /// <returns></returns>
        /// <remarks>2014-2-11 by sutroon</remarks>
        public string GetSelectText()
        {
            return string.IsNullOrEmpty(editor.ActiveTextAreaControl.SelectionManager.SelectedText) ? editor.Text : editor.ActiveTextAreaControl.SelectionManager.SelectedText;
        }

        /// <summary>
        /// 获取编辑器中的SQL命令
        /// </summary>
        /// <returns></returns>
        public string GetText()
        {
            return editor.Text;
        }

        /// <summary>
        /// 设置编辑器中的SQL并决定是否立即执行
        /// </summary>
        /// <param name="text"></param>
        /// <param name="isExec"></param>
        public void SetText(string text, bool isExec)
        {
            editor.Text = text;
            if (isExec && RunScriptHandler != null) RunScriptHandler(text);
        }
        #region 右键菜单
        private void ClickEventHandler()
        {
            // 显隐菜单
            cmsi执行到ToolStripMenuItem.Visible = RunForHandler != null;

            #region 右键菜单
            cmsi保存ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi查看连接ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi撤销ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi打开ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi复制ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi关于ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi剪切ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi清空ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi全选ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi执行ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi执行到ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi粘帖ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi重做ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            cmsi加为收藏ToolStripMenuItem.Click += new EventHandler(ContextMenuItem_Click);
            #endregion
        }
        private void ContextMenuItem_Click(object sender, EventArgs e)
        {
            switch (((ToolStripMenuItem)sender).Name)
            {
                case "cmsi保存ToolStripMenuItem":
                    SaveFileDialog sfd = new SaveFileDialog() { Filter = "Sql文件|(*.sql)|所有文件|(*.*)", FileName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) };
                    if (DialogResult.OK != sfd.ShowDialog()) return;
                    editor.SaveFile(sfd.FileName);
                    break;
                case "cmsi查看连接ToolStripMenuItem":
                    if (DBH != null) MessageBox.Show(DBH.ConnectionString);
                    break;
                case "cmsi撤销ToolStripMenuItem":
                    editor.Undo();
                    break;
                case "cmsi打开ToolStripMenuItem":
                    OpenFileDialog ofd = new OpenFileDialog() { Filter = "Sql文件|(*.sql)|所有文件|(*.*)", FileName = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) };
                    if (DialogResult.OK != ofd.ShowDialog()) return;
                    editor.Text = File.ReadAllText(ofd.FileName);
                    break;
                case "cmsi复制ToolStripMenuItem":
                    editor.ActiveTextAreaControl.TextArea.ClipboardHandler.Copy(null, null);
                    break;
                case "cmsi关于ToolStripMenuItem":
                    if (ShowAboutMessageHandler != null) ShowAboutMessageHandler();
                    //MessageBox.Show(AppDataHelper.GetAboutText(), "关于DBKit5");
                    break;
                case "cmsi剪切ToolStripMenuItem":
                    editor.ActiveTextAreaControl.TextArea.ClipboardHandler.Cut(null, null);
                    break;
                case "cmsi清空ToolStripMenuItem":
                    editor.Text = string.Empty;
                    break;
                case "cmsi全选ToolStripMenuItem":
                    editor.ActiveTextAreaControl.TextArea.ClipboardHandler.SelectAll(null, null);
                    break;
                case "cmsi执行ToolStripMenuItem":
                    if (RunScriptHandler != null) RunScriptHandler(GetSelectText());
                    break;
                case "cmsi执行到ToolStripMenuItem":
                    if (RunForHandler != null) RunForHandler(GetSelectText());
                    break;
                case "cmsi粘帖ToolStripMenuItem":
                    editor.ActiveTextAreaControl.TextArea.ClipboardHandler.Paste(null, null);
                    break;
                case "cmsi重做ToolStripMenuItem":
                    editor.Redo();
                    break;
                case "cmsi加为收藏ToolStripMenuItem":
                    string sqlfav = GetSelectText();
                    if (!ScriptHistorySet.Contains(sqlfav))
                    {
                        if (ScriptHistorySet.Count > 3) ScriptHistorySet.RemoveAt(1);
                        ScriptHistorySet.Add(sqlfav);
                    }
                    break;
            }
        }
        #endregion

        #region 按键事件

        public void AutoCompleteWord(string s)
        {
            // editor.ActiveTextAreaControl.SelectionManager.SelectedText;
            // 获取当前坐标。 
            TextLocation locationStart = this.editor.ActiveTextAreaControl.Caret.Position;

            editor.Document.Insert(editor.ActiveTextAreaControl.Caret.Offset, s);
        }

        private void editor_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == ',')
            {
                e.Handled = true;
                this.ShowSqlIntellisense();
            }
        }

        private void editor_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.F1: // help
                    break;
                case Keys.F5: // execute
                    if (RunScriptHandler != null) RunScriptHandler(GetSelectText());
                    break;
                case Keys.F12: // context tip
                    this.ShowSqlIntellisense();
                    break;
                case Keys.Enter: // so command (run!,save!,load!) [Date,Time,DateTime,Ver,Version,About,Cmd,Md5,Somda,Sodemda,SID,Guid(Empty,New),Lower,Upper]

                    break;
            }
        }
        #endregion
        private void ShowSqlIntellisense()
        {
            frmSqlIntellisense frm = new frmSqlIntellisense() { DBH = this.DBH, TableName = ObjectName };
            frm.ReceiveTextHandler = AutoCompleteWord;
            frm.Show();
        }
        ///   <summary> 
        ///  设置选择文本。
        ///   </summary> 
        ///   <param name="offset"> 选择文本起始处的光标偏移量 </param> 
        ///   <param name="length"> 要选择文本的长度 </param> 
        public void SetSelection(int offset, int length)
        {
            // 将光标至于行首。 
            this.editor.ActiveTextAreaControl.Caret.Column = 0;
            // 根据当前光标所在位置确定是向上移动或向下移动。 
            int x = offset > this.editor.ActiveTextAreaControl.Caret.Offset ? 1 : -1;
            // 首先将光标以行为单位进行上下移动。 
            while (true)
            {
                // 记录上一次的行号。 
                int preLine = this.editor.ActiveTextAreaControl.Caret.Line;
                // 行号加或减1。 
                this.editor.ActiveTextAreaControl.Caret.Line += 1 * x;
                // 如果行号没变，表示已经不能上下移动，跳出。 
                if (this.editor.ActiveTextAreaControl.Caret.Line == preLine)
                    break;

                if (x > 0)
                {
                    // 向下移动且已到到位或超过目标位置，则向上回退一行，以便下面以字符为单位统一向下移动。 
                    if (this.editor.ActiveTextAreaControl.Caret.Offset >= offset)
                    {
                        this.editor.ActiveTextAreaControl.Caret.Line -= 1;
                        break;
                    }
                }
                else
                {
                    // 向下移动且已到到位或超过目标位置则跳出循环。 
                    if (this.editor.ActiveTextAreaControl.Caret.Offset <= offset)
                        break;
                }
            }

            // 以字符为单位向下移动光标以到达指定位置。 
            while (this.editor.ActiveTextAreaControl.Caret.Offset != offset)
            {
                // 记录上一次的位置。 
                int preOffset = this.editor.ActiveTextAreaControl.Caret.Offset;
                this.editor.ActiveTextAreaControl.Caret.Column += 1;

                // 如果到达行尾则向下跳一行。 
                if (this.editor.ActiveTextAreaControl.Caret.Offset == preOffset)
                {
                    this.editor.ActiveTextAreaControl.Caret.Line += 1;
                    this.editor.ActiveTextAreaControl.Caret.Column = 0;
                }
            }

            // 获取当前坐标。 
            TextLocation locationStart = this.editor.ActiveTextAreaControl.Caret.Position;

            // 计算将要选择的量。 
            offset += length;
            offset = Math.Min(offset, this.editor.Text.Length);
            offset = Math.Max(offset, 0);
            this.editor.ActiveTextAreaControl.Caret.Column = 0;

            // 将光标以行为单位进行向下移动。 
            while (true)
            {
                int preLine = this.editor.ActiveTextAreaControl.Caret.Line;
                this.editor.ActiveTextAreaControl.Caret.Line += 1;
                if (this.editor.ActiveTextAreaControl.Caret.Line == preLine)
                    break;

                if (this.editor.ActiveTextAreaControl.Caret.Offset >= offset)
                {
                    this.editor.ActiveTextAreaControl.Caret.Line -= 1;
                    break;
                }
            }

            // 以字符为单位向下移动光标以到达指定位置。 
            while (this.editor.ActiveTextAreaControl.Caret.Offset != offset)
            {
                int preOffset = this.editor.ActiveTextAreaControl.Caret.Offset;
                this.editor.ActiveTextAreaControl.Caret.Column += 1;
                if (this.editor.ActiveTextAreaControl.Caret.Offset == preOffset)
                {
                    this.editor.ActiveTextAreaControl.Caret.Line += 1;
                    this.editor.ActiveTextAreaControl.Caret.Column = 0;
                }
            }

            // 获取当前坐标。 
            TextLocation locationEnd = this.editor.ActiveTextAreaControl.Caret.Position;
            // 设置选择的文本。 
            this.editor.ActiveTextAreaControl.TextArea.SelectionManager.SetSelection(locationStart, locationEnd);
            this.editor.ActiveTextAreaControl.TextArea.ScrollToCaret();
        }

        /// <summary>
        /// 右键菜单打开事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>2014-3-20 by sutroon</remarks>
        private void cmsSQLEditor_Opening(object sender, CancelEventArgs e)
        {
            if (ScriptHistorySet.Count > 0)
            {
                收藏夹ToolStripMenuItem.DropDownItems.Clear();
                ToolStripMenuItem cmi = new ToolStripMenuItem() { Text = "加入收藏(&A)", Name = "cmsi加为收藏ToolStripMenuItem" };
                cmi.Click += new EventHandler(ContextMenuItem_Click);
                收藏夹ToolStripMenuItem.DropDownItems.Add(cmi);
                foreach (string s in ScriptHistorySet)
                {
                    ToolStripMenuItem mi = new ToolStripMenuItem() { Text = s.Length > 16 ? s.Substring(0, 16) : s, Tag = s };
                    mi.Click += new EventHandler(miFav_Click);
                    收藏夹ToolStripMenuItem.DropDownItems.Add(mi);
                }
            }
        }

        // 收藏右键子菜单点击事件 2014-3-20 by sutroon
        private void miFav_Click(object sender, EventArgs e)
        {
            SetText(((ToolStripMenuItem)sender).Tag.ToString(), false);
        }
    }
}
